#include <string>
#include <cstring>
#include <snappy.h>
#include <bitshuffle.h>
#include <bitshuffle_core.h>

extern "C" {
	size_t snappyRawUncompress(char* srcAddr, size_t length, char* destAdd, bool* err) {
		size_t uncompressedLength;
		snappy::GetUncompressedLength(srcAddr, length, &uncompressedLength);
		bool ret = snappy::RawUncompress(srcAddr,length,destAdd);
		if(!ret) {
			*err = false;
			return 0;
 	    }
		return uncompressedLength;
    }

	size_t snappyRawUncompressF32(char* srcAddr, size_t length, char* destAddr, bool* err) {
		return snappyRawUncompress(srcAddr,length,destAddr,err);
    }

	size_t snappyRawUncompressF64(char* srcAddr, size_t length, char destAddr[], bool* err) {
		return snappyRawUncompress(srcAddr,length,destAddr,err);
    }

	size_t snappyRawUncompressI64(char* srcAddr, size_t length, char* destAddr, bool* err) {
		return snappyRawUncompress(srcAddr,length,destAddr,err);
    }

	size_t snappyRawUncompressI32(char* srcAddr, size_t length, char* destAddr, bool* err) {
		return snappyRawUncompress(srcAddr,length,destAddr,err);
    }

	size_t snappyRawUncompressI16(char* srcAddr, size_t length, char* destAddr, bool* err) {
		return snappyRawUncompress(srcAddr,length,destAddr,err);
    }

	size_t snappyRawCompress(char* srcAddr, size_t length, char* destAddr) {
		size_t compressedLength;
		snappy::RawCompress(srcAddr, length, destAddr, &compressedLength);
		return (size_t) compressedLength;
    }

	size_t snappyRawCompress5(char* uncompressed, size_t upos, size_t ulen, char* compressed, size_t cpos) {
		size_t compressedLength;
		snappy::RawCompress(uncompressed + upos, (size_t) ulen, compressed + cpos, &compressedLength);
		return compressedLength;
	}

	size_t snappyRawCompressU8(char* uncompressed, size_t upos, size_t ulen, char* compressed, size_t cpos) {
		size_t compressedLength;
		snappy::RawCompress(uncompressed + upos, ulen, compressed + cpos, &compressedLength);
		return compressedLength;
	}

	size_t snappyRawCompressF64(char* uncompressed, size_t upos, size_t ulen, char* compressed, size_t cpos) {
		size_t compressedLength;
		snappy::RawCompress(uncompressed + upos, (size_t) ulen, compressed + cpos, &compressedLength);
		return compressedLength;
	}

	size_t snappyRawCompressF32(char* uncompressed, size_t upos, size_t ulen, char* compressed, size_t cpos) {
		size_t compressedLength;
		snappy::RawCompress(uncompressed + upos, (size_t) ulen, compressed + cpos, &compressedLength);
		return compressedLength;
	}

	size_t snappyRawCompressI64(char* uncompressed, size_t upos, size_t ulen, char* compressed, size_t cpos) {
		size_t compressedLength;
		snappy::RawCompress(uncompressed + upos, (size_t) ulen, compressed + cpos, &compressedLength);
		return compressedLength;
	}
	
	size_t snappyRawCompressI32(char* uncompressed, size_t upos, size_t ulen, char* compressed, size_t cpos) {
		size_t compressedLength;
		snappy::RawCompress(uncompressed + upos, (size_t) ulen, compressed + cpos, &compressedLength);
		return compressedLength;
	}

	size_t snappyRawCompressI16(char* uncompressed, size_t upos, size_t ulen, char* compressed, size_t cpos) {
		size_t compressedLength;
		snappy::RawCompress(uncompressed + upos, (size_t) ulen, compressed + cpos, &compressedLength);
		return compressedLength;
	}

	size_t snappyRawCompresschar(char* srcAddr, size_t length, char* destAddr) {
		size_t compressedLength;
		snappy::RawCompress(srcAddr, length, destAddr, &compressedLength);
		return compressedLength;
	}

	size_t snappyMaxCompressedLength(size_t size) {
		size_t l = snappy::MaxCompressedLength((size_t) size);
		return l;
	}

	size_t snappyUncompressedLength(char inputAddr[],size_t len) {
		size_t result;
		bool ret = snappy::GetUncompressedLength(inputAddr, len, &result);
		if(!ret) {
			return 0;
		}

		return result;
	}

	size_t snappyUncompressedLengthCpos(char* inputAddr,size_t cpos,size_t clen){
		size_t result;
		bool ret = snappy::GetUncompressedLength(inputAddr + cpos, (size_t) clen, &result);
		return result;
	}

	bool snappyIsValidCompressedBuffer(char inputAddr[], size_t length) {
		bool ret = snappy::IsValidCompressedBuffer(inputAddr, length);
		return ret;
	}

	bool snappyIsValidCompressedBufferCpos(char* inputAddr, size_t cpos, size_t clen) {
		bool ret = snappy::IsValidCompressedBuffer(inputAddr + cpos, clen);
		return ret;
	}

	int64_t bitshuffleBitShuffle(char* input, size_t inputOffset, size_t typeSize, size_t length, char* output, size_t outputOffset)
	{
		int64_t processedBytes = bshuf_bitshuffle(
		    input + inputOffset, output + outputOffset, (size_t) (length / typeSize), (size_t) typeSize, 0);

		return processedBytes;
	}

	int64_t bitshuffleBitShuffleI64(char* input, size_t inputOffset, size_t typeSize, size_t length, char* output, size_t outputOffset)
	{
		int64_t processedBytes = bshuf_bitshuffle(
		    input + inputOffset, output + outputOffset, (size_t) (length / typeSize), (size_t) typeSize, 0);

		return processedBytes;
	}

	int64_t bitshuffleBitShuffleI32(char* input, size_t inputOffset, size_t typeSize, size_t length, char* output, size_t outputOffset)
	{
		int64_t processedBytes = bshuf_bitshuffle(
		    input + inputOffset, output + outputOffset, (size_t) (length / typeSize), (size_t) typeSize, 0);

		return processedBytes;
	}

	int64_t bitshuffleBitShuffleI16(char* input, size_t inputOffset, size_t typeSize, size_t length, char* output, size_t outputOffset)
	{
		int64_t processedBytes = bshuf_bitshuffle(
		    input + inputOffset, output + outputOffset, (size_t) (length / typeSize), (size_t) typeSize, 0);

		return processedBytes;
	}

	int64_t bitshuffleBitShuffleF32(char* input, size_t inputOffset, size_t typeSize, size_t length, char* output, size_t outputOffset)
	{
		int64_t processedBytes = bshuf_bitshuffle(
		    input + inputOffset, output + outputOffset, (size_t) (length / typeSize), (size_t) typeSize, 0);

		return processedBytes;
	}

	int64_t bitshuffleBitShuffleF64(char* input, size_t inputOffset, size_t typeSize, size_t length, char* output, size_t outputOffset)
	{
		int64_t processedBytes = bshuf_bitshuffle(
		    input + inputOffset, output + outputOffset, (size_t) (length / typeSize), (size_t) typeSize, 0);

		return processedBytes;
	}

	int64_t bitshuffleBitUnShuffle(char* input, size_t inputOffset, size_t typeSize, size_t length, char* output, size_t outputOffset)
	{
		int64_t processedBytes = bshuf_bitunshuffle(
                        input + inputOffset, output + outputOffset, (size_t) (length / typeSize), (size_t) typeSize, 0);

		return processedBytes;
	}

	int64_t bitshuffleBitUnShuffleI64(char* input, size_t inputOffset, size_t typeSize, size_t length, char* output, size_t outputOffset)
	{
		int64_t processedBytes = bshuf_bitunshuffle(
                        input + inputOffset, output + outputOffset, (size_t) (length / typeSize), (size_t) typeSize, 0);

		return processedBytes;
	}

	int64_t bitshuffleBitUnShuffleI32(char* input, size_t inputOffset, size_t typeSize, size_t length, char* output, size_t outputOffset)
	{
		int64_t processedBytes = bshuf_bitunshuffle(
                        input + inputOffset, output + outputOffset, (size_t) (length / typeSize), (size_t) typeSize, 0);

		return processedBytes;
	}

	int64_t bitshuffleBitUnShuffleI16(char* input, size_t inputOffset, size_t typeSize, size_t length, char* output, size_t outputOffset)
	{
		int64_t processedBytes = bshuf_bitunshuffle(
                        input + inputOffset, output + outputOffset, (size_t) (length / typeSize), (size_t) typeSize, 0);

		return processedBytes;
	}

	int64_t bitshuffleBitUnShuffleF64(char* input, size_t inputOffset, size_t typeSize, size_t length, char* output, size_t outputOffset)
	{
		int64_t processedBytes = bshuf_bitunshuffle(
                        input + inputOffset, output + outputOffset, (size_t) (length / typeSize), (size_t) typeSize, 0);

		return processedBytes;
	}

	int64_t bitshuffleBitUnShuffleF32(char* input, size_t inputOffset, size_t typeSize, size_t length, char* output, size_t outputOffset)
	{
		int64_t processedBytes = bshuf_bitunshuffle(
                        input + inputOffset, output + outputOffset, (size_t) (length / typeSize), (size_t) typeSize, 0);

		return processedBytes;
	}

	void arrayCopyF64(char* input, size_t offset, size_t length, char* output, size_t output_offset)
	{
		memcpy(output+output_offset, input+offset, length);
	}

	void arrayCopyF64_2(char* input, size_t offset, size_t length, char* output, size_t output_offset)
	{
		memcpy(output+output_offset, input+offset, length);
	}

	void arrayCopyF32(char* input, size_t offset, size_t length, char* output, size_t output_offset)
	{
		memcpy(output+output_offset, input+offset, length);
	}

	void arrayCopyF32_2(char* input, size_t offset, size_t length, char* output, size_t output_offset)
	{
		memcpy(output+output_offset, input+offset, length);
	}

	void arrayCopyI64(char* input, size_t offset, size_t length, char* output, size_t output_offset)
	{
		memcpy(output+output_offset, input+offset, length);
	}

	void arrayCopyI64_2(char* input, size_t offset, size_t length, char* output, size_t output_offset)
	{
		memcpy(output+output_offset, input+offset, length);
	}

	void arrayCopyI32(char* input, size_t offset, size_t length, char* output, size_t output_offset)
	{
		memcpy(output+output_offset, input+offset, length);
	}

	void arrayCopyI32_2(char* input, size_t offset, size_t length, char* output, size_t output_offset)
	{
		memcpy(output+output_offset, input+offset, length);
	}

	void arrayCopyI16(char* input, size_t offset, size_t length, char* output, size_t output_offset)
	{
		memcpy(output+output_offset, input+offset, length);
	}

	void arrayCopyI16_2(char* input, size_t offset, size_t length, char* output, size_t output_offset)
	{
		memcpy(output+output_offset, input+offset, length);
	}
	
}
